package com.ctrip.ec.biz.dao.sequence;

import com.ctrip.ec.biz.entity.sequence.IdRange;
import com.ctrip.ec.biz.entity.sequence.IdResult;
import com.ctrip.ec.dal.DaoBase;
import com.ctrip.ec.biz.entity.sequence.SequenceEntity;
import com.ctrip.ec.dal.DbAccess;
import org.springframework.stereotype.Service;

import java.sql.Date;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Currency;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class SequenceDao extends DaoBase<SequenceEntity> {
    public SequenceDao() {
        super(SequenceEntity.class);
    }

    public SequenceDao(DbAccess db) {
        super(SequenceEntity.class, db);
    }

    private static final Map<String, IdRange> idCache = new ConcurrentHashMap<>();

    private static final int ID_BATCH_COUNT = 50;


    private void generateId(String name, MaxIdMode mode) throws SQLException {
        this.db.begin();
        try {

            String sql = "select *,now() as now  from " + this.getTableName() + " where name='" + name + "' for update";
            List<SequenceEntity> sequenceList = this.db.query(SequenceEntity.class, sql);

            SequenceEntity entity = null;

            Long from = 1L;
            Long to = 1L;
            Timestamp idBelongDate = null; //id属于哪个时间

            if (sequenceList.size() > 0) {
                entity = sequenceList.get(0);
                idBelongDate  =  entity.getNow();
                if (mode == MaxIdMode.Day) {
                    if (entity.getUpdateDate().toString().equals(new Date(entity.getNow().getTime()).toString())) {
                        from = entity.getMaxId() + 1;
                        idBelongDate = new Timestamp(entity.getUpdateDate().getTime());
                    }
                } else if (mode == MaxIdMode.Month) {
                    if (entity.getUpdateDate().toString().substring(0, 6).equals(new Date(entity.getNow().getTime()).toString().substring(0, 6))) {
                        from = entity.getMaxId() + 1;
                        idBelongDate = new Timestamp(entity.getUpdateDate().getTime());
                    }
                } else {
                    from = sequenceList.get(0).getMaxId() + 1;
                    idBelongDate = new Timestamp(entity.getUpdateDate().getTime());
                }

                to = from + ID_BATCH_COUNT;

                sql = "update " + this.getTableName() + " set max_id=" + to + " ,update_date=? where  name='" + name + "'";
                this.db.execute(sql,entity.getNow());

            } else {
                to = from + ID_BATCH_COUNT;
                idBelongDate = new Timestamp(System.currentTimeMillis());
                sql = "insert into " + this.getTableName() + " (name,max_id,update_date) values(?,?,?)";
                this.db.execute(sql, name, to, idBelongDate);
            }

            if(idCache.containsKey(name)){
                idCache.remove(name);
            }
            IdRange range = new IdRange();
            range.setCurrent(from);;
            range.setFrom(from);
            range.setTo(to);;
            range.setTime(idBelongDate);
            idCache.put(name,range);

            this.db.commit();

        } catch (SQLException ex) {
            this.db.rollback();
            throw ex;
        }
    }

//    public SequenceEntity maxIdByMonth(String name) throws SQLException {
//        return maxId(name, MaxIdMode.Month);
//    }

    private  void checkCache(String name,MaxIdMode mode) throws SQLException {
        if(!idCache.containsKey(name)){
            generateId(name,mode);
        }else{
            IdRange range = idCache.get(name);
            if(range.getCurrent().longValue() >range.getTo().longValue()){
                generateId(name,mode);
            }
        }
    }

    public synchronized Long maxId(String name) throws SQLException {
        checkCache(name, MaxIdMode.Global);
        Long maxId = idCache.get(name).getCurrent();
        idCache.get(name).setCurrent(maxId + 1);
        return maxId;
    }


    public synchronized IdResult maxIdByDay(String name) throws SQLException {

        checkCache(name, MaxIdMode.Day);
        IdRange range=  idCache.get(name);
        Long maxId = range.getCurrent();
        range.setCurrent(maxId + 1);

        IdResult result = new IdResult();
        result.setMaxId(maxId);
        result.setNow(range.getTime());
        return result;
    }


    public enum MaxIdMode {
        Global,
        Day,
        Month
    }
}
